package model

import (
	"context"
	"errors"
	"fmt"
	"gozero-microservices/user-service/rpc/utils"
	"reflect"
	"strconv"
	"strings"

	"github.com/zeromicro/go-zero/core/stores/sqlx"
)

/*
 +----------------------------------------------------------------------
 + Title        : BaseModel
 + Author       : huwl
 + Version      : 1.0.0
 + Initial-Time : 2019-08-08
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : 基础模型
 + 				: 生成模型命令: goctl model mysql datasource -url="root:19891201@tcp(127.0.0.1:3306)/trident_mall" -table="mall_region" -dir internal/model -c=false -style goZero
 +----------------------------------------------------------------------
*/

type (
	BaseModel interface {
		InsertTx(ctx context.Context, tx sqlx.Session, params interface{}) (int64, error)
		BatchInsertTx(ctx context.Context, tx sqlx.Session, params []interface{}) error
		BatchAdd(ctx context.Context, params []interface{}) (int64, error)
		BatchAddWithTransaction(ctx context.Context, params []interface{}) error
		BatchInsertChunk(ctx context.Context, params []interface{}, chunkSize int) (int64, error)
		DeleteByParams(ctx context.Context, params map[string]interface{}) error
		EditByPrimaryKey(ctx context.Context, params map[string]interface{}, primaryKeyValue interface{}, primaryKeyName ...string) error
		EditByPrimaryKeyWithTx(ctx context.Context, tx sqlx.Session, params map[string]interface{}, primaryKeyValue interface{}, primaryKeyName ...string) error
		EditByParams(ctx context.Context, params, wheres map[string]interface{}) error
		EditByParamsWithTx(ctx context.Context, tx sqlx.Session, params, wheres map[string]interface{}) error
		GetResultsByTotal(ctx context.Context, params map[string]interface{}) (int64, error)
		GetResultsByParams(ctx context.Context, result any, params map[string]interface{}, page uint32, pageSize int8, order string) error
		Row(ctx context.Context, result any, params map[string]interface{}) error
		Conn() sqlx.SqlConn
	}

	defaultBaseModel struct {
		conn  sqlx.SqlConn
		table string
	}

	Base struct{}
)

/**
 * mysql conn
 *
 * @return sqlx.SqlConn
 * @author huwl
 * @date 2025/10/24 14:30:24
 */
func (m *defaultBaseModel) Conn() sqlx.SqlConn {
	return m.conn
}

/**
 * 事务新增
 *
 * @param context.Context ctx --------------------------必传,上下文
 * @param sqlx.Session tx ------------------------------必传,sqlx
 * @param interface{} params ---------------------------必传,新增数据
 * @return int64, error
 * @author huwl
 * @date 2025/10/24 14:30:24
 */
func (m *defaultBaseModel) InsertTx(ctx context.Context, tx sqlx.Session, params interface{}) (int64, error) {
	t := reflect.TypeOf(params)
	v := reflect.ValueOf(params)

	// 参数字段
	paramsFields := []string{}

	// 占位符
	placeholders := []string{}

	// 参数值
	paramsValues := []interface{}{}

	// 组装参数字段 + 占位符 + 参数值
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		value := v.Field(i)

		// 组装字段
		paramsFields = append(paramsFields, field.Tag.Get("db"))

		// 组装占位符
		placeholders = append(placeholders, "?")

		// 组装参数值
		paramsValues = append(paramsValues, value.Interface())
	}

	// 组装sql
	query := fmt.Sprintf(
		"INSERT INTO %s ("+strings.Join(paramsFields, ",")+") VALUES %s",
		m.table,
		"("+strings.Join(placeholders, ",")+")",
	)

	result, err := tx.ExecCtx(ctx, query, paramsValues...)
	if err != nil {
		return 0, err
	}

	return result.LastInsertId()
}

/**
 * 事务新增【用于逻辑层参与跨表事务操作】
 *
 * @param context.Context ctx --------------------------必传,上下文
 * @param sqlx.Session tx ------------------------------必传,sqlx
 * @param []interface{} params -------------------------必传,新增数据
 * @return error
 * @author huwl
 * @date 2025/10/24 14:30:24
 */
func (m *defaultBaseModel) BatchInsertTx(ctx context.Context, tx sqlx.Session, params []interface{}) error {
	if len(params) == 0 {
		return errors.New("新增数据不能为空")
	}

	// 参数字段
	paramsFields := []string{}

	// 占位符
	placeholders := []string{}

	// 参数值
	paramsValues := []interface{}{}

	// 组装新增数据
	for key, value := range params {
		t := reflect.TypeOf(value)
		v := reflect.ValueOf(value)

		// 单条数据占位符
		placeholderItems := []string{}

		// 组装字段 + 单条数据占位符 + 单条数据值
		for i := 0; i < t.NumField(); i++ {
			field := t.Field(i)
			value := v.Field(i)
			// fmt.Printf("Field: %s, Value: %v\n", field.Tag.Get("db"), value.Interface())
			if key == 0 {
				paramsFields = append(paramsFields, field.Tag.Get("db"))
			}

			// 组装单条数据占位符
			placeholderItems = append(placeholderItems, "?")
			paramsValues = append(paramsValues, value.Interface())
		}

		// 组装多条数据占位符
		placeholders = append(placeholders, "("+strings.Join(placeholderItems, ",")+")")
	}

	// 组装sql
	query := fmt.Sprintf(
		"INSERT INTO %s ("+strings.Join(paramsFields, ",")+") VALUES %s",
		m.table,
		strings.Join(placeholders, ", "),
	)

	// 执行sql
	_, err := tx.ExecCtx(ctx, query, paramsValues...)
	return err
}

/**
 * 批量新增
 *
 * @param context.Context ctx --------------------------必传,上下文
 * @param []interface{} params -------------------------必传,新增数据
 * @return int64, error
 * @author huwl
 * @date 2025/10/24 14:30:24
 */
func (m *defaultBaseModel) BatchAdd(ctx context.Context, params []interface{}) (int64, error) {
	if len(params) == 0 {
		return 0, errors.New("新增数据不能为空")
	}

	// 参数字段
	paramsFields := []string{}

	// 占位符
	placeholders := []string{}

	// 参数值
	paramsValues := []interface{}{}

	// 组装新增数据
	for key, value := range params {
		t := reflect.TypeOf(value)
		v := reflect.ValueOf(value)

		// 单条数据占位符
		placeholderItems := []string{}

		// 组装字段 + 单条数据占位符 + 单条数据值
		for i := 0; i < t.NumField(); i++ {
			field := t.Field(i)
			value := v.Field(i)
			// fmt.Printf("Field: %s, Value: %v\n", field.Tag.Get("db"), value.Interface())
			if key == 0 {
				paramsFields = append(paramsFields, field.Tag.Get("db"))
			}

			// 组装单条数据占位符
			placeholderItems = append(placeholderItems, "?")
			paramsValues = append(paramsValues, value.Interface())
		}

		// 组装多条数据占位符
		placeholders = append(placeholders, "("+strings.Join(placeholderItems, ",")+")")
	}

	// 组装sql
	query := fmt.Sprintf(
		"INSERT INTO %s ("+strings.Join(paramsFields, ",")+") VALUES %s",
		m.table,
		strings.Join(placeholders, ", "),
	)

	// 执行插入
	result, err := m.conn.ExecCtx(context.Background(), query, paramsValues...)
	if err != nil {
		return 0, err
	}

	return result.RowsAffected()
}

/**
 * 带事务的批量新增【单表事务操作】
 *
 * @param context.Context ctx --------------------------必传,上下文
 * @param []interface{} params -------------------------必传,新增数据
 * @return error
 * @author huwl
 * @date 2025/10/24 14:30:24
 */
func (m *defaultBaseModel) BatchAddWithTransaction(ctx context.Context, params []interface{}) error {
	if len(params) == 0 {
		return errors.New("新增数据不能为空")
	}

	return m.conn.TransactCtx(context.Background(), func(ctx context.Context, s sqlx.Session) error {
		// 参数字段
		paramsFields := []string{}

		// 占位符
		placeholders := []string{}

		// 参数值
		paramsValues := []interface{}{}

		// 组装新增数据
		for key, value := range params {
			t := reflect.TypeOf(value)
			v := reflect.ValueOf(value)

			// 单条数据占位符
			placeholderItems := []string{}

			// 组装字段 + 单条数据占位符 + 单条数据值
			for i := 0; i < t.NumField(); i++ {
				field := t.Field(i)
				value := v.Field(i)
				// fmt.Printf("Field: %s, Value: %v\n", field.Tag.Get("db"), value.Interface())
				if key == 0 {
					paramsFields = append(paramsFields, field.Tag.Get("db"))
				}

				// 组装单条数据占位符
				placeholderItems = append(placeholderItems, "?")
				paramsValues = append(paramsValues, value.Interface())
			}

			// 组装多条数据占位符
			placeholders = append(placeholders, "("+strings.Join(placeholderItems, ",")+")")
		}

		// 组装sql
		query := fmt.Sprintf(
			"INSERT INTO %s ("+strings.Join(paramsFields, ",")+") VALUES %s",
			m.table,
			strings.Join(placeholders, ", "),
		)

		// 执行sql
		_, err := s.ExecCtx(ctx, query, paramsValues...)
		return err
	})
}

/**
 * 分块批量插入，避免单次插入数据量过大
 *
 * @param []interface{} params -------------------------必传,新增数据
 * @param int chunkSize --------------------------------必传,块大小
 * @return int64, error
 * @author huwl
 * @date 2025/10/24 14:30:24
 */
func (m *defaultBaseModel) BatchInsertChunk(ctx context.Context, params []interface{}, chunkSize int) (int64, error) {
	if len(params) == 0 {
		return 0, errors.New("新增数据不能为空")
	}

	var totalAffected int64

	// 分块处理
	for i := 0; i < len(params); i += chunkSize {
		end := i + chunkSize
		if end > len(params) {
			end = len(params)
		}

		chunk := params[i:end]
		affected, err := m.BatchAdd(ctx, chunk)
		if err != nil {
			return totalAffected, err
		}

		totalAffected += affected
	}

	return totalAffected, nil
}

/**
 * 根据条件删除
 *
 * @param context.Context ctx --------------------------必传,上下文
 * @param map[string]interface{} params ----------------必传,删除参数
 * @return error
 */
func (m *defaultBaseModel) DeleteByParams(ctx context.Context, params map[string]interface{}) error {
	query := fmt.Sprintf("DELETE FROM %s", m.table)

	// 参数值
	var values []interface{}
	// 如果有查询条件，添加 WHERE 子句
	if len(params) > 0 {
		query += " WHERE"
		// 拼接sql语句
		for key, value := range params {
			keys := strings.Split(key, " ")
			if len(keys) == 3 {
				// 处理IN查询占位符
				if strings.ToUpper(keys[1]) == "IN" {
					// 生成占位符: ?, ?, ?
					placeholders := strings.Repeat("?, ", len(value.([]int))-1) + "?"
					query += fmt.Sprintf(" " + keys[0] + " IN (" + placeholders + ") AND")
				} else {
					query += fmt.Sprintf(" " + key + " AND")
				}
			} else if len(strings.Split(key, " ")) == 1 {
				query += fmt.Sprintf(" " + key + " = ? AND")
			}

			// 处理IN查询值
			if len(keys) == 3 && strings.ToUpper(keys[1]) == "IN" {
				for _, v := range value.([]int) {
					values = append(values, v)
				}
			} else {
				values = append(values, value)
			}
		}
		query = strings.TrimRight(query, " AND")
	} else {
		return errors.New("删除必须要有条件")
	}

	// 执行sql
	_, err := m.conn.ExecCtx(ctx, query, values...)
	return err
}

/**
 * 根据主键更新数据
 *
 * @param context.Context ctx --------------------------必传,上下文
 * @param map[string]interface{} params ----------------必传,更新数据
 * @param interface{} primaryKeyValue ------------------必传,主键值
 * @param []string primaryKeyName ----------------------非必传,主键名
 * @return error
 * @author huwl
 */
func (m *defaultBaseModel) EditByPrimaryKey(ctx context.Context, params map[string]interface{}, primaryKeyValue interface{}, primaryKeyName ...string) error {
	primaryKey := "id"
	if len(primaryKeyName) > 0 {
		primaryKey = primaryKeyName[0]
	}

	// 组装更新数据
	params["update_time"] = utils.GetDate()
	paramsFields := []string{}
	paramsValues := []interface{}{}
	for k, v := range params {
		paramsFields = append(paramsFields, k)
		paramsValues = append(paramsValues, v)
	}

	// 拼接sql语句
	query := fmt.Sprintf("UPDATE %s SET %s", m.table, strings.Join(paramsFields, "=?,")+"=?") + " WHERE " + primaryKey + " = ?"

	// 合并更新、条件参数
	paramsValues = append(paramsValues, primaryKeyValue)

	// 执行sql
	_, err := m.conn.ExecCtx(ctx, query, paramsValues...)
	return err
}

/**
 * 事务根据主键更新数据【用于逻辑层参与跨表事务操作】
 *
 * @param context.Context ctx --------------------------必传,上下文
 * @param sqlx.Session tx ------------------------------必传,sqlx
 * @param map[string]interface{} params ----------------必传,更新数据
 * @param interface{} primaryKeyValue ------------------必传,主键值
 * @param []string primaryKeyName ----------------------非必传,主键名
 * @return error
 * @author huwl
 */
func (m *defaultBaseModel) EditByPrimaryKeyWithTx(ctx context.Context, tx sqlx.Session, params map[string]interface{}, primaryKeyValue interface{}, primaryKeyName ...string) error {
	primaryKey := "id"
	if len(primaryKeyName) > 0 {
		primaryKey = primaryKeyName[0]
	}

	// 组装更新数据
	params["update_time"] = utils.GetDate()
	paramsFields := []string{}
	paramsValues := []interface{}{}
	for k, v := range params {
		paramsFields = append(paramsFields, k)
		paramsValues = append(paramsValues, v)
	}

	// 拼接sql语句
	query := fmt.Sprintf("UPDATE %s SET %s", m.table, strings.Join(paramsFields, "=?,")+"=?") + " WHERE " + primaryKey + " = ?"

	// 合并更新、条件参数
	paramsValues = append(paramsValues, primaryKeyValue)

	// 执行sql
	_, err := tx.ExecCtx(ctx, query, paramsValues...)
	return err
}

/**
 * 根据条件更新数据
 *
 * @param context.Context ctx --------------------------必传,上下文
 * @param map[string]interface{} params ----------------必传,更新数据
 * @param map[string]interface{} wheres ----------------必传,更新条件
 * @return error
 */
func (m *defaultBaseModel) EditByParams(ctx context.Context, params, wheres map[string]interface{}) error {
	// 组装更新数据
	params["update_time"] = utils.GetDate()
	paramsFields := []string{}
	paramsValues := []interface{}{}
	for k, v := range params {
		paramsFields = append(paramsFields, k)
		paramsValues = append(paramsValues, v)
	}

	query := fmt.Sprintf("UPDATE %s SET %s", m.table, strings.Join(paramsFields, "=?,")+"=?")

	// 组装更新条件数据
	var whereValues []interface{}
	if len(wheres) > 0 {
		for key, value := range wheres {
			query += " WHERE"
			// 拼接sql语句
			keys := strings.Split(key, " ")
			if len(keys) == 3 {
				// 处理IN查询占位符
				if strings.ToUpper(keys[1]) == "IN" {
					// 生成占位符: ?, ?, ?
					placeholders := strings.Repeat("?, ", len(value.([]int))-1) + "?"
					query += fmt.Sprintf(" " + keys[0] + " IN (" + placeholders + ") AND")
				} else {
					query += fmt.Sprintf(" " + key + " AND")
				}
			} else if len(strings.Split(key, " ")) == 1 {
				query += fmt.Sprintf(" " + key + " = ? AND")
			}

			// 处理IN查询值
			if len(keys) == 3 && strings.ToUpper(keys[1]) == "IN" {
				for _, v := range value.([]int) {
					whereValues = append(whereValues, v)
				}
			} else {
				whereValues = append(whereValues, value)
			}
			query = strings.TrimRight(query, " AND")
		}
	}

	// 合并更新、条件参数
	merged := append(paramsValues, whereValues...)

	// 执行sql
	_, err := m.conn.ExecCtx(ctx, query, merged...)
	return err
}

/**
 * 事务根据条件更新数据【用于逻辑层参与跨表事务操作】
 *
 * @param context.Context ctx --------------------------必传,上下文
 * @param sqlx.Session tx ------------------------------必传,sqlx
 * @param map[string]interface{} params ----------------必传,更新数据
 * @param map[string]interface{} wheres ----------------必传,更新条件
 * @return error
 */
func (m *defaultBaseModel) EditByParamsWithTx(ctx context.Context, tx sqlx.Session, params, wheres map[string]interface{}) error {
	// 组装更新数据
	params["update_time"] = utils.GetDate()
	paramsFields := []string{}
	paramsValues := []interface{}{}
	for k, v := range params {
		paramsFields = append(paramsFields, k)
		paramsValues = append(paramsValues, v)
	}

	query := fmt.Sprintf("UPDATE %s SET %s", m.table, strings.Join(paramsFields, "=?,")+"=?")

	// 组装更新条件数据
	var whereValues []interface{}
	if len(wheres) > 0 {
		for key, value := range wheres {
			query += " WHERE"
			// 拼接sql语句
			keys := strings.Split(key, " ")
			if len(keys) == 3 {
				// 处理IN查询占位符
				if strings.ToUpper(keys[1]) == "IN" {
					// 生成占位符: ?, ?, ?
					placeholders := strings.Repeat("?, ", len(value.([]int))-1) + "?"
					query += fmt.Sprintf(" " + keys[0] + " IN (" + placeholders + ") AND")
				} else {
					query += fmt.Sprintf(" " + key + " AND")
				}
			} else if len(strings.Split(key, " ")) == 1 {
				query += fmt.Sprintf(" " + key + " = ? AND")
			}

			// 处理IN查询值
			if len(keys) == 3 && strings.ToUpper(keys[1]) == "IN" {
				for _, v := range value.([]int) {
					whereValues = append(whereValues, v)
				}
			} else {
				whereValues = append(whereValues, value)
			}
			query = strings.TrimRight(query, " AND")
		}
	}

	// 合并更新、条件参数
	merged := append(paramsValues, whereValues...)

	// 执行sql
	_, err := tx.ExecCtx(ctx, query, merged...)
	return err
}

/**
 * 根据条件获取数量
 *
 * @param context.Context ctx --------------------------必传,上下文
 * @param map[string]interface{} params ----------------必传,查询条件
 * @return int64 error
 */
func (m *defaultBaseModel) GetResultsByTotal(ctx context.Context, params map[string]interface{}) (int64, error) {
	var resp int64
	query := fmt.Sprintf("SELECT count(*) FROM %s", m.table)

	// 参数值
	var values []interface{}

	// 如果有查询条件，添加 WHERE 子句
	if len(params) > 0 {
		query += " WHERE"
		// 拼接sql语句
		for key, value := range params {
			keys := strings.Split(key, " ")
			if len(keys) == 3 {
				// 处理IN查询占位符
				if strings.ToUpper(keys[1]) == "IN" {
					// 生成占位符: ?, ?, ?
					placeholders := strings.Repeat("?, ", len(value.([]int))-1) + "?"
					query += fmt.Sprintf(" " + keys[0] + " IN (" + placeholders + ") AND")
				} else {
					query += fmt.Sprintf(" " + key + " AND")
				}
			} else if len(strings.Split(key, " ")) == 1 {
				query += fmt.Sprintf(" " + key + " = ? AND")
			}

			// 处理IN查询值
			if len(keys) == 3 && strings.ToUpper(keys[1]) == "IN" {
				for _, v := range value.([]int) {
					values = append(values, v)
				}
			} else {
				values = append(values, value)
			}
		}
		query = strings.TrimRight(query, " AND")
	}

	// 执行sql
	err := m.conn.QueryRowCtx(ctx, &resp, query, values...)
	if err != nil {
		return 0, err
	}
	return resp, nil
}

/**
 * 根据条件获取信息
 *
 * @param context.Context ctx --------------------------必传,上下文
 * @param interface{} result ---------------------------必传，返回结果
 * @param map[string]interface{} params ----------------必传,查询条件
 * @param uint32 page ----------------------------------必传,当前页
 * @param int8 pageSize --------------------------------必传,条数
 * @param string order ---------------------------------必传，"age desc, name"支持多个
 * @return error
 */
func (m *defaultBaseModel) GetResultsByParams(ctx context.Context, result interface{}, params map[string]interface{}, page uint32, pageSize int8, order string) error {
	query := fmt.Sprintf("SELECT * FROM %s", m.table)

	// 参数值
	var values []interface{}
	// 如果有查询条件，添加 WHERE 子句
	if len(params) > 0 {
		query += " WHERE"
		// 拼接sql语句
		for key, value := range params {
			keys := strings.Split(key, " ")
			if len(keys) == 3 {
				// 处理IN查询占位符
				if strings.ToUpper(keys[1]) == "IN" {
					// 生成占位符: ?, ?, ?
					placeholders := strings.Repeat("?, ", len(value.([]int))-1) + "?"
					query += fmt.Sprintf(" " + keys[0] + " IN (" + placeholders + ") AND")
				} else {
					query += fmt.Sprintf(" " + key + " AND")
				}
			} else if len(strings.Split(key, " ")) == 1 {
				query += fmt.Sprintf(" " + key + " = ? AND")
			}

			// 处理IN查询值
			if len(keys) == 3 && strings.ToUpper(keys[1]) == "IN" {
				for _, v := range value.([]int) {
					values = append(values, v)
				}
			} else {
				values = append(values, value)
			}
		}
		query = strings.TrimRight(query, " AND")
	}

	// 拼接排序
	query += " ORDER BY " + order

	// 拼接分页
	if pageSize > 0 {
		offset := (page - 1) * uint32(pageSize)
		query += " LIMIT " + strconv.Itoa(int(offset)) + "," + strconv.Itoa(int(pageSize))
	}

	// 执行sql
	return m.conn.QueryRowsCtx(ctx, result, query, values...)
}

/**
 * 单条数据
 * db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
 * Where("name <> ?", "jinzhu")
 * Where("name IN ?", []string{"jinzhu", "jinzhu 2"})
 * Where("name LIKE ?", "%jin%")
 * Where("name = ? AND age >= ?", "jinzhu", "22")
 * Where("updated_at > ?", lastWeek)
 * Where("created_at BETWEEN ? AND ?", lastWeek, today)
 *
 * @param context.Context ctx --------------------------必传,上下文
 * @param interface{} result ---------------------------必传
 * @param map[string]interface{} params ----------------必传
 * @return error
 */
func (m *defaultBaseModel) Row(ctx context.Context, result interface{}, params map[string]interface{}) error {
	query := fmt.Sprintf("SELECT * FROM %s", m.table)

	// 参数值
	var values []interface{}
	// 如果有查询条件，添加 WHERE 子句
	if len(params) > 0 {
		query += " WHERE"
		// 拼接sql语句
		for key, value := range params {
			keys := strings.Split(key, " ")
			if len(keys) == 3 {
				// 处理IN查询占位符
				if strings.ToUpper(keys[1]) == "IN" {
					// 生成占位符: ?, ?, ?
					placeholders := strings.Repeat("?, ", len(value.([]int))-1) + "?"
					query += fmt.Sprintf(" " + keys[0] + " IN (" + placeholders + ") AND")
				} else {
					query += fmt.Sprintf(" " + key + " AND")
				}
			} else if len(strings.Split(key, " ")) == 1 {
				query += fmt.Sprintf(" " + key + " = ? AND")
			}

			// 处理IN查询值
			if len(keys) == 3 && strings.ToUpper(keys[1]) == "IN" {
				for _, v := range value.([]int) {
					values = append(values, v)
				}
			} else {
				values = append(values, value)
			}
		}
		query = strings.TrimRight(query, " AND")
	}

	// 拼接分页
	query += " LIMIT 1"

	// 执行sql
	return m.conn.QueryRowCtx(ctx, result, query, values...)
}
